﻿using FlyingEye.Common.Utils;
using FlyingEye.Consts;
using FlyingEye.ESApplication.Services;
using FlyingEye.LogInformations;
using FlyingEye.MySqlApplication;
using FlyingEye.MySqlApplication.Models;
using FlyingEye.Users;
using FlyingEye.Users.Exceptions;
using FlyingEye.Users.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using System.Linq.Dynamic.Core;
using System.Security.Claims;
using Volo.Abp.ObjectMapping;

namespace FlyingEye.Services
{
    internal class UserOrderService : IUserOrderService
    {
        private readonly IUserCreationOrderRepository _orderRepository;

        private readonly IObjectMapper _mapper;

        private readonly IUserRepository _userRepository;

        private readonly IUserService _userService;

        private readonly ApplicationConfiguration _configuration;

        private readonly ILogger<UserOrderService> _logger;

        private readonly IHttpContextAccessor _httpContextAccessor;

        private readonly ILogService _logService;

        public UserOrderService(IUserCreationOrderRepository orderRepository,
            IObjectMapper mapper,
            IUserRepository userRepository,
            IUserService userService,
            ApplicationConfiguration configuration,
            ILogger<UserOrderService> logger,
            IHttpContextAccessor httpContextAccessor,
            ILogService logService)
        {
            _logService = logService;
            _orderRepository = orderRepository;
            _mapper = mapper;
            _userRepository = userRepository;
            _userService = userService;
            _configuration = configuration;
            _logger = logger;
            _httpContextAccessor = httpContextAccessor;
        }

        /// <summary>
        /// 创建一个用户创建工单
        /// </summary>
        public async Task CreateAsync(UserCreationOrder order)
        {
            if (string.IsNullOrWhiteSpace(order.UserName))
            {
                throw new InvalidUserNameException();
            }

            var password = string.Empty;

            try
            {
                // 密码必须是正确加密后的密文。
                password = _configuration.ConvertFromEncryptedBase64String(order.Password);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "密文解密错误。");
                throw new InvalidPasswordException("密码格式错误，请阅读 API 文档。");
            }

            // 进行密码校验。
            var result = PasswordValidator.Core.EvaluatePassword(password);

            if (result.Score < 3)
            {
                throw new InvalidPasswordException();
            }

            // 先查询用户名是否已经被占用
            var existingUser = await _userRepository.GetUserByNameAsync(order.UserName);

            if (existingUser != null)
            {
                throw new UserAlreadyExistsException();
            }

            // 查询工单是否使用用户名
            var existingOrder = await _orderRepository.FindAsync(orderItem => orderItem.UserName == order.UserName);

            if (existingOrder != null)
            {
                throw new UserCreationOrderAlreadyExistsException();
            }

            var model = _mapper.Map<UserCreationOrder, UserCreationOrderModel>(order);
            await _orderRepository.InsertAsync(model);
            await LogInformation(model.Id, UserOrderActionNames.Create, info => info.NewUserName = model.UserName);
        }

        /// <summary>
        /// 审批一个用户工单
        /// </summary>
        public async Task ApproveAsync(ApprovedUserCreationOrder order)
        {
            var existingOrder = await _orderRepository.FindAsync(orderItem => orderItem.Id == order.Id);

            if (existingOrder == null)
            {
                throw new UserCreationOrderNotExistException();
            }

            // 将工单状态设置为已经审批
            existingOrder.IsApproved = true;
            existingOrder.IsAgree = order.IsAgree;

            await _orderRepository.UpdateAsync(existingOrder, true);
            await LogInformation(existingOrder.Id, UserOrderActionNames.Approve, info =>
            {
                info.IsAgree = order.IsAgree;
                info.NewUserName = existingOrder.UserName;
            });

            // 注册新用户
            if (order.IsAgree)
            {
                var newUser = _mapper.Map<UserCreationOrderModel, User>(existingOrder);
                // 这个 API 不能创建 admin 类型的用户
                await _userService.RegisterUserAsync(newUser);
            }
        }

        /// <summary>
        /// 获取所有未审批的创建用户的工单
        /// </summary>
        /// <param name="input">分页输入</param>
        public async Task<IEnumerable<UnapprovedUserCreationOrder>> GetAllUnapprovedOrdersAsync(UnapprovedUserCreationOrderInput input)
        {
            var queryable = await _orderRepository.GetQueryableAsync();
            var modelList = await queryable.Where(user => user.IsApproved == false)
               .OrderBy(user => user.UserName)
               .PageBy(input.SkipCount, input.MaxResultCount)
               .ToListAsync();

            if (modelList == null)
            {
                return new List<UnapprovedUserCreationOrder>(0);
            }

            return _mapper.Map<List<UserCreationOrderModel>, List<UnapprovedUserCreationOrder>>(modelList);
        }

        private async Task LogInformation(Guid orderId, string actionName, Action<UserOrderLogInformation>? action = null)
        {
            var userId = _httpContextAccessor.HttpContext?.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;

            if (Guid.TryParse(userId, out var id))
            {
                var user = await _userService.GetUserByIdAsync(id);
                var info = new LogInformations.UserOrderLogInformation(orderId, actionName, id, user.UserName);
                action?.Invoke(info);
                await _logService.LogUserOrderInformation(info);
            }
            else
            {
                var info = new LogInformations.UserOrderLogInformation(orderId, actionName, Guid.Empty, String.Empty);
                action?.Invoke(info);
                await _logService.LogUserOrderInformation(info);
            }
        }
    }
}
